package okw.packager;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;

import okw.communication.CommunicationService;
import okw.database.DatabaseService;
import okw.database.model.MessagePackage;

import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import core.communication.message.Message;
import core.communication.message.VotersPackageMessage;
import core.communication.message.VotesPackageMessage;

@Service
public class PackagerServiceImpl implements PackagerService {

	private DatabaseService databaseService;
	private CommunicationService communicationService;
	private PackagerThread packagerThread;
	private ThreadPoolTaskExecutor taskExecutor;

	private static int packageTimeout = 60000;

	@Inject
	public PackagerServiceImpl(DatabaseService databaseService, ThreadPoolTaskExecutor taskExecutor) {
		Assert.notNull(databaseService);
		Assert.notNull(taskExecutor);

		this.databaseService = databaseService;
		this.taskExecutor = taskExecutor;
		packagerThread = new PackagerThread();
	}

	@Override
	public void stopPackagerThread() {
		packagerThread.stop();
	}

	@Override
	public void setCommunicationService(CommunicationService communicationService) {
		this.communicationService = communicationService;
		taskExecutor.execute(packagerThread);
	}

	@Override
	public void handlePackage(Message message) {
		databaseService.storePackage(message);
	}

	private class PackagerThread implements Runnable {

		private volatile Thread thread = null;

		public PackagerThread() {
			thread = new Thread(this, "packager");
		}

		public void stop() {
			thread = null;
		}

		@Override
		public void run() {
			while (thread != null) {
				Map<Long, List<MessagePackage>> packagesMap = new HashMap<Long, List<MessagePackage>>();
				List<MessagePackage> packages = databaseService.getVotesPackages();

				for (MessagePackage pm : packages) {
					VotesPackageMessage p = (VotesPackageMessage) pm.getMessageObj();
					List<MessagePackage> packagesList = packagesMap.get(p.getElectionIdentificationField());
					if (packagesList == null) {
						packagesList = new ArrayList<MessagePackage>();
						packagesList.add(pm);
						packagesMap.put(p.getElectionIdentificationField(), packagesList);
					} else {
						packagesList.add(pm);
					}
				}

				for (List<MessagePackage> packagesList : packagesMap.values()) {
					if (!packagesList.isEmpty()) {
						VotesPackageMessage p = (VotesPackageMessage) packagesList.get(0).getMessageObj();
						sendVotesPackage(p.getElectionIdentificationField(), packagesList);
					}
				}

				List<MessagePackage> votersPackages = databaseService.getVotersPackages();

				if (!votersPackages.isEmpty()) {
					VotersPackageMessage p = (VotersPackageMessage) votersPackages.get(0).getMessageObj();
					sendVotersPackage(p.getElectionIdentificationField(), votersPackages);
				}

				try {
					Thread.sleep(packageTimeout);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

		@Transactional
		private void sendVotesPackage(Long electionIdentificationField, List<MessagePackage> packages) {
			VotesPackageMessage message = new VotesPackageMessage(electionIdentificationField, communicationService.getServerURI());
			for (MessagePackage p : packages)
				message.getPackages().add((VotesPackageMessage) p.getMessageObj());
			databaseService.deletePackages(packages);
			communicationService.send(message);
		}

		@Transactional
		private void sendVotersPackage(Long electionIdentificationField, List<MessagePackage> packages) {
			VotersPackageMessage message = new VotersPackageMessage(electionIdentificationField);
			for (MessagePackage p : packages)
				message.getVotersIds().addAll(((VotersPackageMessage) p.getMessageObj()).getVotersIds());
			databaseService.deletePackages(packages);
			communicationService.send(message);
		}
	}
}
